package xyz.thoughtset.viewer.executor.blocks.executor;

import cn.hutool.core.convert.Convert;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import xyz.thoughtset.viewer.common.exc.entity.ExcInfo;
import xyz.thoughtset.viewer.common.exc.exceptions.ExecException;
import xyz.thoughtset.viewer.modules.fun.constants.FunParamTypeConstant;
import xyz.thoughtset.viewer.modules.fun.entity.FunInfo;
import xyz.thoughtset.viewer.modules.fun.entity.FunParam;
import xyz.thoughtset.viewer.modules.fun.service.FunInfoService;
import xyz.thoughtset.viewer.modules.step.entity.block.BlockInfo;
import xyz.thoughtset.viewer.modules.step.service.BlockInfoService;
import xyz.thoughtset.viewer.modules.step.service.BlockInfoService;

import java.util.*;

@Component
public class BlockExecutorManager {
    @Autowired
    private BlockInfoService blockInfoService;
    @Autowired
    private FunInfoService funInfoService;

    public Object execBlocks(String funId, Map params) {
        return execBlocks(funId, params, new SpelExpressionParser(), new StandardEvaluationContext());
    }
    public Object execBlocks(String funId,Map params,ExpressionParser parser){
        return execBlocks(funId, params, parser, new StandardEvaluationContext());
    }
    public Object execBlocks(String funId,Map params,ExpressionParser parser,StandardEvaluationContext context){
        if (ObjectUtils.isEmpty(params)){
            params = new HashMap<>();
        }
        FunInfo funInfo = funInfoService.getById(funId);
        if (!StringUtils.hasText(funId) || funInfo == null) return null;
        List<BlockInfo> blocks = blockInfoService.listByPId(funId);
        Map contentMap = beforeQuery(params, funInfo,context);
        for (BlockInfo block : blocks) {
            Object queryResult;
            try{
                blockInfoService.loadBlockEles(block);
                queryResult = ExecutorRegistry.findExecutor(block.getBodyType())
                        .executeQuery(block, parser, contentMap,context);
            }catch (ExecException e){
                throw e.addApiId(funId);
            }catch (Exception e){
                e.printStackTrace();
                ExecException exc = ExcInfo.buildAndThrowExc(e,params);
                exc.addBlockId(block.getId());
                exc.addApiId(funId);
                throw exc;
            }
            String key = block.getTitle();
            if (!StringUtils.hasText(key)){
                if (queryResult instanceof Map){
                    for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) queryResult).entrySet()) {
                        key = Convert.convert(String.class, entry.getKey());
                        contentMap.put(key, entry.getValue());
                        context.setVariable(key, entry.getValue());
                    }
                }
                continue;
            }
            Object val = queryResult;
            if (block.resultWasSingle() && !ObjectUtils.isEmpty(queryResult)){
                if (val instanceof List){
                    val = ((List)queryResult).get(0);
                }
            }else{
                if (!(val instanceof Collection)){
                    val = new ArrayList<>();
                    ((ArrayList) val).add(queryResult);
                }
            }
            contentMap.put(key,val);
            context.setVariable(key, val);
        }

        return afterQuery(contentMap, funInfo,context);
    }


    protected Map beforeQuery(Map<String, Object> searchMap, FunInfo funInfo,StandardEvaluationContext context) {
        if (funInfo == null) {
            throw new NullPointerException("fun not found");
        }
        //过滤入参+临时变量
        Map paramMap = new HashMap<>();
        List<FunParam> inputParams = funInfo.getGroupParams().get(FunParamTypeConstant.INPUT);
        if(!ObjectUtils.isEmpty(inputParams) && !ObjectUtils.isEmpty(searchMap)){
            paramMap = new LinkedHashMap<>();
            for(FunParam param : inputParams) {
                String key = param.getTitle();
                Object paramValue = searchMap.get(key);
                if (paramValue == null) {
                    paramValue = searchMap.get(param.getId());
                }
                if (paramValue == null) {
                    paramValue = param.getDefaultVal();
                }
                context.setVariable(key, paramValue);
                paramMap.put(key, paramValue);
            }
        }
        List<FunParam> innerParams = funInfo.getGroupParams().get(FunParamTypeConstant.INNER);
        if (!ObjectUtils.isEmpty(innerParams)){
            for (FunParam param : innerParams) {
                String key = param.getTitle();
                Object paramValue = param.getDefaultVal();
                paramMap.put(key, paramValue);
                context.setVariable(key, paramValue);
            }
        }
        return paramMap;
    }
    protected Object afterQuery(Map<String, Object> result, FunInfo funInfo,StandardEvaluationContext context) {
        List<FunParam> resultParams = funInfo.getGroupParams().get(FunParamTypeConstant.OUTPUT);
        if(!ObjectUtils.isEmpty(resultParams)){
            if (resultParams.size() == 1 ){
                return result.get(resultParams.get(0).getTitle());
            }
            Map<String,Object> resultMap = new HashMap<>();
            for (FunParam resultParam : resultParams) {
                resultMap.put(resultParam.getTitle(),result.get(resultParam.getTitle()));
            }
            return resultMap;
        }
        return result;
    }

    protected Map afterQuery(FunInfo funInfo,ExpressionParser parser,StandardEvaluationContext context) {
        List<FunParam> resultParams = funInfo.getGroupParams().get(FunParamTypeConstant.OUTPUT);
        if(!ObjectUtils.isEmpty(resultParams)){
            Map<String,Object> resultMap = new HashMap<>();
            for (FunParam resultParam : resultParams) {
                resultMap.put(resultParam.getTitle(),parser.parseExpression(resultParam.getTitle()).getValue(context));
//                Object value = parser.parseExpression(exp).getValue(context);
            }
            return resultMap;
        }
        return null;
    }

}
